<template>
  <div ref="videoBox" class="play-video">
    <video
      :id="id"
      :ref="id"
      :bind-id="info['DeviceID']"
      :style="{
        'width': '100%',
        'height': '100%',
        'background-color': 'black'
      }"
      muted
      autoplay
    />
  </div>
</template>

<script>
import axios from 'axios'
import moment from 'moment'
import { uuid } from 'vue-uuid'
import { Janus } from '../../../player/components/ivms/libs/janus_streaming'
import { defaultState } from '../../../../mixins'

export default {
  name: 'PlayVideo',
  mixins: [defaultState],
  props: {
    info: {
      type: Object,
      default: () => {
      }
    },
    objectFit: {
      type: String,
      default: 'contain'
    },
    authorization: {
      type: String,
      default: ''
    },
    serverType: {
      type: Number,
      default: 1
    },
    server: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      id: uuid.v1(),
      errors: {
        NETWORK_NOT_CONNECTED: { code: 101, msg: '检测到未连接到网络.' },
        ICE_DISCONNECTED: { code: 102, msg: '与ICE服务器断开连接.' },
        SERVER_DISCONNECTED: { code: 103, msg: '与流媒体服务器断开连接.' },
        FUNCTION_ERROR_CALL: { code: 104, msg: '服务器接口调用错误.' },
        PARAMETER_ERROR_SERVER_TYPE: { code: 201, msg: '参数错误.错误的服务器类型.' },
        PARAMETER_ERROR_CHANNEL: { code: 202, msg: '参数错误.错误的通道号.' },
        PARAMETER_ERROR_AUTHORIZATION: { code: 203, msg: '参数错误.未检测到认证码.' },
        PARAMETER_ERROR_SERVER_WS: { code: 204, msg: '参数错误.Websocket参数错误.' },
        PARAMETER_ERROR_SERVER_STREAM: { code: 205, msg: '参数错误.流媒体服务参数错误.' },
        KEEPALIVE_ERROR: { code: 301, msg: '流保活错误.' },
        INIT_PC_ERROR: { code: 302, msg: 'RTCPeerConnect初始化错误.' },
        RECONNECT_ERROR: { code: 303, msg: '重连失败.' }
      },
      serverTypeList: {
        MRS: 1,
        SMS: 2
      },
      streamServer: '',
      wsServer: '',
      iceServer: '',
      janus: null,
      pc: null,
      pcTimer: null,
      RTCId: null,
      sessionId: '',
      isPlaying: false,
      isConnected: false,
      reconnectTimer: null,
      reconnectCount: 0,
      isKeepAlive: false,
      aliveTime: 60,
      keepAliveTimer: null,
      keepaliveCount: 0,
      isDown: false,
      startPosition: {
        x: 0,
        y: 0
      },
    }
  },
  watch: {
    info: {
      handler: function(val) {
        if (this.info.uuid === this.layoutSelected.uuid) {
          const time = moment().hour(0).minute(this.info.time).format('HH:mm')
          if (Object.keys(this.info.replay).indexOf(time) >= 0) {
            if (this.isManual) {
              this.init()
            }
          } else {
            this.stop()
          }
        }
      },
      deep: true
    }
  },
  created() {
    this.init()
  },
  beforeUpdate() {
    new Promise(resolve => {
      this.stop()
      resolve(true)
    }).then(flag => {
      this.init()
    })
  },
  beforeDestroy() {
    this.stop()
  },
  methods: {
    formatTime() {
      return moment().hour(0).minute(this.info.time).format('HH:mm')
    },
    // 初始化
    init() {
      this.stop()
      if (this.judgeNetworkStatus()) {
        this.changeIsManual(false)
        if (Object.keys(this.info.replay).indexOf(this.formatTime()) >= 0) {
          this.$emit('message', { type: 'ready' })
          this.mrsConnect()
        }
      } else {
        this.$emit('message', { type: 'error', msg: this.errors.NETWORK_NOT_CONNECTED.msg })
      }
    },
    // MRS连接
    mrsConnect() {
      const _now = this.info.replay[this.formatTime()]
      if (this.info.uuid === this.layoutSelected.uuid) {
        axios.get(`${this.server}/ivms/sipsvr/vod2`, {
          params: {
            chanpubid: _now.DeviceID,
            streamtranstype: 6,
            starttime: _now.StartTime,
            endtime: _now.EndTime,
            AuthorizationId: this.authorization
          }
        }).then(res => {
          if (res.data.SessionId) {
            this.sessionId = res.data.SessionId
            this.isPlaying = true
            this.keepaliveCount = 0
            this.isKeepAlive = true
            //  视频保活
            this.keepAlive()
            this.mrsPlay(res.data.rtcurl)
          } else {
            this.$emit('message', { type: 'disconnect', msg: this.errors.FUNCTION_ERROR_CALL.msg })
            this.stop()
          }
        }).catch((err) => {
          this.$emit('message', { type: 'error', msg: err })
          this.stop()
        })
      }
    },
    // MRS连接播放
    mrsPlay(address) {
      const _this = this
      const urlObject = _this.parseUrl(address)
      const schema = window.location.protocol
      this.pcDestroy()
      // 创建RTC的P2P通道
      // 在Chrome 82及更高版本下，必须增加{ sdpSemantics: 'unified-plan' }参数
      _this.pc = new RTCPeerConnection({ sdpSemantics: 'unified-plan' })
      // 监听收流事件
      _this.pc.onaddstream = (event) => {
        if (_this.$refs[_this.id]) {
          _this.$refs[_this.id].srcObject = event.stream
        } else {
          this.stop()
          this.reconnect()
        }
      }
      // 监听ice连接状态
      _this.pc.oniceconnectionstatechange = ev => {
        if (_this.pc.iceConnectionState === 'disconnected' || _this.pc.iceConnectionState === 'failed') {
          _this.reconnect()
        } else if (_this.pc.iceConnectionState === 'connected') {
          // 连接成功后，重连次数清零
          _this.reconnectCount = 0
        }
      }
      // 开始握手
      new Promise(function(resolve, reject) {
        _this.pc.addTransceiver('audio', { direction: 'recvonly' })
        _this.pc.addTransceiver('video', { direction: 'recvonly' })

        _this.pc.createOffer(function(offer) {
          resolve(offer)
        }, function(reason) {
          reject(reason)
        })
      }).then(function(offer) {
        return _this.pc.setLocalDescription(offer).then(() => {
          return offer
        }).catch(() => {
          _this.reconnect()
        })
      }).then(function(offer) {
        return new Promise(function(resolve, reject) {
          const port = urlObject.port || 1985
          let api = urlObject.user_query.play || '/rtc/v1/play/'
          if (api.lastIndexOf('/') !== api.length - 1) {
            api += '/'
          }
          let url = schema + '//' + urlObject.server + '' +
            ':' + port + api
          for (const key in urlObject.user_query) {
            if (key !== 'api' && key !== 'play') {
              url += '&' + key + '=' + urlObject.user_query[key]
            }
          }
          url = url.replace(api + '&', api + '?')
          const data = {
            api: url,
            streamurl: urlObject.url,
            clientip: null,
            sdp: offer.sdp
          }
          axios.post(url, JSON.stringify(data)).then(res => {
            // console.log('Got answer: ', res.data)
            resolve(res.data.sdp)
          }).catch(err => {
            reject(err)
          })
        })
      }).then(function(answer) {
        _this.isConnected = true
        return _this.pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: answer }))
      }).catch(function(reason) {
        _this.$emit('message', { type: 'error', msg: _this.errors.INIT_PC_ERROR.msg })
        _this.isConnected = true
        // throw reason
        _this.reconnect()
      })
      this.$emit('play')
    },
    /**
     * 解析server参数
     * 仅用于服务器类型为SMS时
     */
    smsParseServer() {
      if (this.serverType === 2) {
        if (!this.server) {
          return false
        } else {
          if (this.server.ws) {
            const { serverIp, serverPort } = this.server.ws
            this.wsServer = `ws://${serverIp}:${serverPort}`
          } else {
            this.$emit('message', { type: 'error', msg: this.errors.PARAMETER_ERROR_SERVER_WS.msg })
          }
          // ICE服务器配置，只有在公网时需要，内网下可不配置
          if (this.server.ice) {
            const { serverIp, serverPort, userName, password } = this.server.ice
            this.iceServer = [{
              urls: `turn:${serverIp}:${serverPort}`,
              userName: userName,
              credential: password
            }]
          }
          if (this.server.stream) {
            const { serverIp, serverPort } = this.server.stream
            this.streamServer = `http://${serverIp}:${serverPort}`
          } else {
            this.$emit('message', { type: 'error', msg: this.errors.PARAMETER_ERROR_SERVER_STREAM.msg })
          }
        }
      }
    },
    smsConnect() {
      const self = this
      new Promise(resolve => {
        const janus = new Janus({
          server: this.wsServer,
          iceServer: this.iceServer,
          onlogin: data => {
            self.isConnected = true
            self.janus = janus
            self.RTCId = data.loginId
            self.$emit('message', { type: 'ready', msg: data })
            resolve()
          },
          onremotestream: stream => {
            const session = Janus.sessions[self.janus.getsessionId()]
            session.attachMediaStream(self.el, stream)
            self.$emit('message', { type: 'stream', msg: stream })
          },
          // ICE状态监听
          // 相当于RTCPeerConnection.oniceconnectionstatechange监听
          // 返回ICE连接状态
          iceState: state => {
            // 此处默认不做处理，在error中做处理
          },
          error: async err => {
            self.$emit('message', { type: 'error', msg: err })
            self.reconnect()
          }
        })
      }).then(() => {
        self.smsPlay()
      })
    },
    smsPlay() {
      if (!this.isConnected) {
        this.$emit('message', { type: 'error', msg: this.errors.SERVER_DISCONNECTED.msg })
        return
      }
      if (this.isPlaying) {
        this.stop()
      }
      const self = this
      new Promise((resolve, reject) => {
        axios.get(`${this.streamServer}/ivms/realplay2?chanpubid=${this.info[this.treeNodeKey]}&streamtranstype=4&webrtcid=${this.RTCId}&AuthorizationId=${this.authorization}`).then(res => {
          resolve(res)
        }).catch(err => {
          reject(err)
        })
      }).then(resp => {
        self.sessionId = resp.data.SessionId
        self.isPlaying = true
        this.isKeepAlive = true
        self.keepalive()
        self.pc = self.janus.gethandle().webrtcStuff.pc
        if (!self.pc) {
          self.pcKeepalive()
        }
        self.$emit('play')
      }).catch(err => {
        self.$emit('message', { type: 'error', msg: this.errors.FUNCTION_ERROR_CALL.msg })
        self.reconnect()
      })
    },
    // pc保活
    pcKeepalive() {
      const self = this
      this.pcTimer = setInterval(() => {
        self.pc = self.janus.gethandle().webrtcStuff.pc
        if (self.pc) {
          clearInterval(self.pcTimer)
        }
      }, 100)
    },
    // 视频保活
    keepAlive() {
      if (!this.isPlaying) return
      if (!this.reconnectTimer && this.isKeepAlive) {
        this.keepAliveTimer = setInterval(() => {
          if (this.sessionId) {
            axios.get(`${this.server}/ivms/video_keeplive?SessionId=${this.sessionId}&AuthorizationId=${this.authorization}`).then(resp => {
              if (resp.data.errcode === 0) {
                if (this.aliveTime !== resp.data.Keeplive) {
                  this.keepaliveFailCount = 0
                  this.aliveTime = resp.data.Keeplive
                  clearInterval(this.keepAliveTimer)
                  this.keepAliveTimer = null
                }
              } else {
                if (this.keepaliveCount > 3) {
                  this.stop()
                  this.keepaliveCount = 0
                  return false
                }
                this.keepaliveCount++
              }
            }).catch(() => {
              this.$emit('message', { type: 'error', msg: this.errors.KEEPALIVE_ERROR.msg })
              this.stop()
            })
          } else {
            this.stop()
          }
        }, this.aliveTime * 1000 / 2)
      }
    },
    // 立即重连
    connectNow() {
      this.reconnectCount = 0
      this.$emit('message', { type: 'reconnect', msg: { count: this.reconnectCount, time: 0 } })
      this.connectMain()
    },
    // 重连
    reconnect() {
      if (!this.reconnectTimer) {
        if (this.maxReconnectTimes === 0) {
          this.connectAgain()
        } else if (this.maxReconnectTimes > 0) {
          // 判定重连失败次数是否大于最大重连次数
          if (this.reconnectCount >= this.maxReconnectTimes) {
            this.stop()
            this.$emit('message', { type: 'disconnect', msg: '超过最大重连次数' })
            return false
          }
          this.connectAgain()
        } else {
          this.stop()
        }
      }
    },
    connectAgain() {
      // 停止当前在播视频
      this.stop()
      this.reconnectCount++
      // 设置本次重连等待时间
      let time = this.reconnectDuration * this.reconnectCount + 1
      this.reconnectTimer = setInterval(() => {
        time--
        // 等待时间结束后执行
        if (time <= 0) {
          clearInterval(this.reconnectTimer)
          this.reconnectTimer = null
          this.sessionId = ''
          this.connectMain()
        }
        this.$emit('message', { type: 'reconnect', msg: { count: this.reconnectCount, time: time } })
      }, 1000)
    },
    connectMain() {
      new Promise(resolve => {
        this.stop()
        resolve(this.sessionId)
      }).then(sessionId => {
        if (!sessionId) {
          if (this.serverType === 1) {
            this.mrsConnect()
          } else if (this.serverType === 2) {
            this.smsConnect()
          }
        }
      })
    },
    // 停止播放
    stop() {
      this.isPlaying = false
      this.isConnected = false
      if (this.sessionId) {
        axios.get(`${this.server}/ivms/close_vod?SessionId=${this.sessionId}&AuthorizationId=${this.authorization}`).then(() => {
          this.$emit('close')
        }).catch(err => {
          console.log(err)
          this.$emit('close')
        })
      }
      this.destroy()
      this.$emit('stop')
    },
    // 销毁PC
    pcDestroy() {
      if (this.pc) {
        this.pc.onaddstream = null
        this.pc.oniceconnectionstatechange = null
        this.pc.addTransceiver = null
        this.pc.createOffer = null
        this.pc.setLocalDescription = null
        this.pc.setRemoteDescription = null
        this.pc.close()
        this.pc = null
      }
    },
    // 销毁定时器
    destroy() {
      if (this.isPlaying || this.isConnected) {
        this.stop()
      }
      if (this.serverType === 1) {
        this.pcDestroy()
      } else {
        if (this.janus) {
          this.janus.destroySession()
          // this.janus.destroyWebsocket()
        }
        this.janus = null
      }
      if (this.$parent.timer) {
        this.$emit('destroy-timer')
      }
      this.sessionId = ''
      // 重连计时器
      clearInterval(this.reconnectTimer)
      this.reconnectTimer = null
      // 保活计时器
      this.isKeepAlive = false
      clearInterval(this.keepAliveTimer)
      this.keepAliveTimer = null
      this.keepaliveCount = 0
      this.aliveTime = 60
    },
    // 判定当前网络状态
    judgeNetworkStatus() {
      return !!window.navigator.onLine
    },
    // 转换url
    parseUrl(url) {
      const a = document.createElement('a')
      a.href = url.replace('webrtc://', 'http://').replace('rtc://', 'http://')

      let vhost = a.hostname
      let app = a.pathname.substr(1, a.pathname.lastIndexOf('/') - 1)
      const stream = a.pathname.substr(a.pathname.lastIndexOf('/') + 1)

      app = app.replace('...vhost...', '?vhost=')
      if (app.indexOf('?') >= 0) {
        const params = app.substr(app.indexOf('?'))
        app = app.substr(0, app.indexOf('?'))

        if (params.indexOf('vhost=') > 0) {
          vhost = params.substr(params.indexOf('vhost=') + 'vhost='.length)
          if (vhost.indexOf('&') > 0) {
            vhost = vhost.substr(0, vhost.indexOf('&'))
          }
        }
      }
      if (a.hostname === vhost) {
        const re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
        if (re.test(a.hostname)) {
          vhost = '__defaultVhost__'
        }
      }
      let schema = 'rtmp'
      if (url.indexOf('://') > 0) {
        schema = url.substr(0, url.indexOf('://'))
      }
      let port = a.port
      if (!port) {
        if (schema === 'webrtc' || schema === 'rtc') {
          port = 1985
        }
      }
      const ret = {
        url: url,
        schema: schema,
        server: a.hostname,
        port: port,
        vhost: vhost,
        app: app,
        stream: stream
      }
      this.fillQuery(a.search, ret)
      return ret
    },
    fillQuery(query_string, obj) {
      obj.user_query = {}
      if (query_string.length === 0) {
        return
      }
      if (query_string.indexOf('?') >= 0) {
        query_string = query_string.split('?')[1]
      }
      const queries = query_string.split('&')
      for (let i = 0; i < queries.length; i++) {
        const elem = queries[i]

        const query = elem.split('=')
        obj[query[0]] = query[1]
        obj.user_query[query[0]] = query[1]
      }
      if (obj.domain) {
        obj.vhost = obj.domain
      }
    },
    // 鼠标拖动操作
    mouseDown(e) {
      this.isDown = true
      this.startPosition = {
        x: e.pageX,
        y: e.pageY
      }
      this.$emit('change-selected')
    },
    mouseMove(e) {
      if (this.isDown) {
        const _x = e.pageX - this.startPosition.x
        const _y = e.pageY - this.startPosition.y
        const _z = Math.abs(_x) - Math.abs(_y)
        let type = 0
        if (_z < 0) {
          if (Math.abs(_y) > 20) {
            if (_y > 0) {
              type = 2
            } else if (_y < 0) {
              type = 1
            }
          }
        } else {
          if (Math.abs(_x) > 20) {
            if (_x > 0) {
              type = 4
            } else if (_x < 0) {
              type = 3
            }
          }
        }
        if (type !== 0 && this.type !== type) {
          if (this.layoutSelected.cameraInfo[this.treeNodeKey]) {
            new Promise(resolve => {
              this.ptzControl(0)
              resolve(type)
            }).then(type => {
              this.type = type
              this.ptzControl(type)
            })
          }
        }
      }
    },
    mouseUp(e) {
      this.isDown = false
      this.startPosition = {
        x: 0,
        y: 0
      }
      if (this.layoutSelected.cameraInfo[this.treeNodeKey] && this.type !== 0) {
        this.type = 0
        this.ptzControl(0)
      }
    },
    ptzControl(operation) {
      const url = `${this.layoutConfig.enableHTTPS ? 'https://' : 'http://'}${this.layoutConfig.server.stream.ip}:${this.layoutConfig.server.stream.port}`
      axios.get(`${url}/ivms/ptz`, {
        params: {
          ChanPubId: this.layoutSelected.cameraInfo[this.treeNodeKey],
          PtzType: operation,
          PtzParam: 120,
          AuthorizationId: this.authorization
        }
      })
    },
    // 更改video的object-fit
    changeFit(e) {
      document.getElementById(this.id).style.objectFit = e
    }
  }
}
</script>

<style scoped lang="scss">
.play-video {
  width: 100%;
  height: 100%;

  video {
    object-fit: contain;
  }
}
</style>
